﻿//**********************************************************************//
//  Name: Form1                                                         //
//  Description: The main form that is created for the Structure Entry  //
//              Control System.                                         //
//  Author: Scott Duntley (sdd2857@rit.edu)                             //
//**********************************************************************//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Xml.Serialization;
using System.Net.Sockets;
using System.Threading;


namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        // CONSTANTS
        // Message types
        const string ALERT = "alert";
        const string COMMAND = "command";
        const string CONFIG = "config";

        // Alert message types
        const string ENTRANT_DETECTED = "entrantDetectedLAN";
        const string ALERT_CANCELED = "canceled";
        const string TIMEOUT = "timeout";
        const string ACCESS_GRANTED = "accessGranted";

        // Command message types
        const string DOOR = "door";
        // DOOR message actions
        const string GRANT_ACCESS = "grantAccess";
        const string DENY_ACCESS = "denyAccess";

        // Configuration message types
        const string LOGIN = "login";
        const string LOGIN_FAILED = "loginFailed";
        const string LOGIN_SUCCESS = "loginSuccess";
        const string NEW_KEY = "newKey";
        const string SET_USERNAME = "username";
        const string SET_PASSWORD = "password";
        const int RECEIVE_PORT = 8081;

        // Boolean for controlling the red flashing when an alert is received
        bool isRed;

        // Used to send messages to the control device
        UdpClient controlDeviceClient;
        IPEndPoint controlDeviceEndPoint;

        // IP and Port for the control device
        String controlDeviceIPString;
        byte[] controlDeviceIP = new byte[4];
        String controlDevicePortString;
        int controlDevicePort;

        // Used to received messages back from the control device
        UdpClient localUDPClient;
        IPEndPoint receiveEndPoint;

        // The door key received from the control device
        String doorKey;

        bool loginSuccess = false;

        public static Form2 optionsForm = null;

        //***********************************************************************
        //  Name: Form1()
        //  Description: Sets up some parameters and starts the form          
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        public Form1()
        {
            InitializeComponent();
        }

        //***********************************************************************
        //  Name: InitializeConnection()
        //  Description: Initializes a UDP connection with the IpAddress and port
        //          specified in the options menu.
        //  Author: Scott Duntley (sdd2857@rit.edu) 
        //***********************************************************************
        private void InitializeConnection()
        {
            // Sets up the ip address for the control device
            controlDeviceIP[0] = 0;
            String[] tmp = controlDeviceIPString.Split('.');
            for (int i = 0; i < tmp.Length; i++)
            {
                controlDeviceIP[i] = Convert.ToByte(tmp[i]);
            }
            controlDevicePort = Convert.ToInt32(controlDevicePortString);

            IPAddress ipAddress = new IPAddress(controlDeviceIP);
            controlDeviceEndPoint = new IPEndPoint(ipAddress, controlDevicePort);

            // For sending messages
            controlDeviceClient = new UdpClient();
            controlDeviceClient.Connect(controlDeviceEndPoint);

            // For receiving messages
            receiveEndPoint = new IPEndPoint(IPAddress.Any, 0);
            localUDPClient = new UdpClient(RECEIVE_PORT);
        }


        //***********************************************************************
        //  Name: SendMessage()
        //  Description: Sends a message to the control device
        //  Parameters: msg - string - the message to send
        //  Returns: true - Success,  - Failure
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private bool SendMessage(string msg)
        {
            // Convert the intended message to bytes and send it
            Byte[] sendBytes = Encoding.ASCII.GetBytes(msg);
            try
            {
                controlDeviceClient.Send(sendBytes, sendBytes.Length);
                return true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
                return false;
            }

        }

        //***********************************************************************
        //  Name: ReceiveMessage()
        //  Description: Spins waiting for a message to be received from the
        //          control device.
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        public void ReceiveMessage()
        {
            string msg, msgType; 
            string[] msgArray;

            char[] msgDelim = new char[] { ':', ',', ';' };
            imageCaptureEnable();
            try
            {
                for (; ; )
                {
                    msg = Encoding.ASCII.GetString(localUDPClient.Receive(ref receiveEndPoint));                    
                    msgArray = msg.Split(msgDelim);
                    msgType = msgArray[0];
                    switch (msgArray[0])
                    {
                        case ALERT:
                            if (msgArray[1] == ENTRANT_DETECTED)
                            {                                
                                timerEnable();
                                if (optionsForm.imageRadio.Checked)
                                {
                                    imageCaptureTimer.Interval = Convert.ToInt32(optionsForm.imageCapturePeriodTextBox.Text) * 1000; 
                                    imageCaptureEnable();
                                }
                                // Pass the received timestamp
                                if (loginSuccess)
                                {
                                    EntrantDetected(msgArray[2]);
                                }
                            }
                            else if (msgArray[1] == ALERT_CANCELED)
                            {                                
                                alertTimer.Enabled = false;
                                imageCaptureTimer.Enabled = false;
                                backgroundButton.BackColor = Color.DarkGreen;                                
                                if (msgArray[2] == TIMEOUT)
                                {
                                    MessageBox.Show("Timeout occurred");
                                }
                                else if (msgArray[2] == ACCESS_GRANTED)
                                {
                                    MessageBox.Show("Access was granted");
                                }
                            }
                            else
                            {
                                // show dialog for invalid message
                                MessageBox.Show("Invalid message\n" + msg);
                            }
                            break;
                            
                        case COMMAND:
                            // show dialog for invalid message. PC App should not receive any command messages
                            MessageBox.Show("Invalid message\n" + msg);
                            break;

                        case CONFIG:
                            if (msgArray[1] == LOGIN_FAILED)
                            {
                                LoginStatus(false);
                            }
                            else if (msgArray[1] == LOGIN_SUCCESS)
                            {
                                LoginStatus(true);
                                NewKey(msgArray[2]);
                            }
                            else if (msgArray[1] == NEW_KEY)
                            {
                                // Pass the new key received
                                if (loginSuccess)
                                {
                                    NewKey(msgArray[2]);
                                }
                            }
                            else
                            {
                                // show dialog for invalid message
                                MessageBox.Show("Invalid message\n" + msg);
                            }
                            break;
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                System.Windows.Forms.Application.Exit();
                System.Environment.Exit(1);
            }
        }

        //***********************************************************************
        //  Name: webBrowser1_DocumentCompleted()
        //  Description: Called when the web browser for the video is loaded.
        //          Makes the web browser visible and viewing the correct window.          
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
        {
            // Only execute this code if we are already logged in
            if (loginSuccess)
            {
                VideoFeed.Visible = true;
                VideoFeed.Document.BackColor = Color.Black;
                VideoFeed.Document.Window.ScrollTo(150, 345);
                allowButton.Enabled = true;
                denyButton.Enabled = true;
                captureButton.Enabled = true;
                optionsButton.Enabled = true;
                userNameTextBox.Enabled = false;
                passwordTextBox.Enabled = false;
                loginButton.Visible = false;
            }
        }


        //***********************************************************************
        //  Name: showWebBrowser
        //  Description: Loads the video feed                  
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void showWebBrowser()
        {
            VideoFeed.Navigate("http://admin:secs@129.21.27.69:81/");
        }

        //***********************************************************************
        //  Name: allowButton_click()
        //  Description: Called when the allow button is clicked                  
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void allowButton_click(object sender, EventArgs e)
        {
            if (!SendMessage(COMMAND + ":" + DOOR + "," + doorKey + "," + GRANT_ACCESS + ";"))
            {
                MessageBox.Show("Command could not be sent.");
            }
        }

        //***********************************************************************
        //  Name: denyButton_click()
        //  Description: Called when the deny button is clicked              
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void denyButton_click(object sender, EventArgs e)
        {
            if (!SendMessage(COMMAND + ":" + DOOR + "," + doorKey + "," + DENY_ACCESS + ";"))
            {
                MessageBox.Show("Command could not be sent.");
            }
        }        

        //***********************************************************************
        //  Name: captureButton_click()
        //  Description: Called when the capture button is clicked.
        //          Saves the current image to the directory specified by the
        //          options menu                    
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void captureButton_click(object sender, EventArgs e)
        {
            captureImage();
        }

        //***********************************************************************
        //  Name: optionsButton_click()
        //  Description: Opens the options form
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void optionsButton_click(object sender, EventArgs e)
        {
            DialogResult result = optionsForm.ShowDialog(this);
            if (result == DialogResult.Cancel)
            {
                // Save all settings if the save button was pressed
                optionsForm.imageCaptureLocationTextBox.Text = Properties.Settings.Default.ImageCaptureLocation;
                optionsForm.setIPTextBox.Text = Properties.Settings.Default.IPAddress;
                optionsForm.setPortTextBox.Text = Properties.Settings.Default.Port;
                optionsForm.imageCapturePeriodTextBox.Text = Properties.Settings.Default.ImageCapturePeriod;
                optionsForm.doorUnlockTimeTextBox.Text = Properties.Settings.Default.DoorUnlockTime;
                optionsForm.imageRadio.Checked = Properties.Settings.Default.ImageRadioChecked;
            }
            else if (result == DialogResult.OK)
            {
                // Save all settings if the save button was pressed
                Properties.Settings.Default.ImageCaptureLocation = optionsForm.imageCaptureLocationTextBox.Text;
                Properties.Settings.Default.IPAddress = optionsForm.setIPTextBox.Text;
                Properties.Settings.Default.Port = optionsForm.setPortTextBox.Text;
                Properties.Settings.Default.ImageCapturePeriod = optionsForm.imageCapturePeriodTextBox.Text;
                Properties.Settings.Default.DoorUnlockTime = optionsForm.doorUnlockTimeTextBox.Text;
                Properties.Settings.Default.ImageRadioChecked = optionsForm.imageRadio.Checked;
                Properties.Settings.Default.Save();
                if (!loginSuccess)
                {
                    System.Diagnostics.Process.Start(Application.ExecutablePath); // to start new instance of application
                    this.Close(); //to turn off current app
                }
            }
        }


        private void openFileDialog1_FileOk(object sender, CancelEventArgs e) 
        {

        }

        //***********************************************************************
        //  Name: Form1_Load()
        //  Description: Called after the form is first created
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void Form1_Load(object sender, EventArgs e)
        {
            MessageBox.Show("Please Login");
            alertTimer.Start();
            alertTimer.Enabled = false;
            imageCaptureTimer.Start();
            imageCaptureTimer.Enabled = false;
            backgroundButton.BackColor = Color.DarkGreen;
            // creates the option form
            optionsForm = new Form2();

            // Grabs the ip address and port that was previously saved
            controlDeviceIPString = Properties.Settings.Default.IPAddress;
            //controlDeviceIPString = "192.168.1.2";
            controlDevicePortString = Properties.Settings.Default.Port;
            //controlDevicePortString = "8080";
            if (controlDeviceIPString != "")
            {
                InitializeConnection();

                // Start the receive message thread
                Thread receiveMessageThread = new Thread(ReceiveMessage);
                receiveMessageThread.Start();
            }

            doorKey = "";
            
            // load some values from the previous configuration
            optionsForm.setIPTextBox.Text = Properties.Settings.Default.IPAddress;
            optionsForm.setPortTextBox.Text = Properties.Settings.Default.Port;
            optionsForm.imageCapturePeriodTextBox.Text = Properties.Settings.Default.ImageCapturePeriod;
            optionsForm.doorUnlockTimeTextBox.Text = Properties.Settings.Default.DoorUnlockTime;
            optionsForm.imageCaptureLocationTextBox.Text = Properties.Settings.Default.ImageCaptureLocation;
        }


        //***********************************************************************
        //  Name: alertTimer_Tick()
        //  Description: A timer set to change the border of the video to alternating
        //          red
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void alertTimer_Tick(object sender, EventArgs e)
        {
            if (isRed == false)
            {
                backgroundButton.BackColor = Color.Red;
                isRed = true;
            }
            else
            {
                backgroundButton.BackColor = Color.DarkGreen;
                isRed = false;
            }
        }

        //***********************************************************************
        //  Name: EntrantDetected()
        //  Description: Handles a guest detection
        //  Parameters: timestamp - the time at which the entrant was detected
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void EntrantDetected(string timestamp)
        {
            alertTimer.Enabled = true;
        }

        //***********************************************************************
        //  Name: LoginFailed()
        //  Description: Handles a login failed message
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void LoginStatus(bool result)
        {
            if (result == false)
            {
                MessageBox.Show("Login Failed: Please try again");
                loginSuccess = false;
            }
            else
            {
                loginSuccess = true;
                backgroundButton.BackgroundImage = null;
                showWebBrowser();
            }
        }

        //***********************************************************************
        //  Name: NewKey()
        //  Description: Sets the new key for connecting to the control device
        //  Parameters: key - string - the new key to set
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void NewKey(string key)
        {
            doorKey = key;
        }

        //***********************************************************************
        //  Name: Form1_Closed()
        //  Description: Is called when the program is closed
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void Form1_Closed(object sender, FormClosedEventArgs e)
        {
            try
            {
                // Save all settings from the current run
                Properties.Settings.Default.ImageCaptureLocation = optionsForm.imageCaptureLocationTextBox.Text;
                Properties.Settings.Default.IPAddress = optionsForm.setIPTextBox.Text;
                Properties.Settings.Default.Port = optionsForm.setPortTextBox.Text;
                Properties.Settings.Default.ImageCapturePeriod = optionsForm.imageCapturePeriodTextBox.Text;
                Properties.Settings.Default.DoorUnlockTime = optionsForm.doorUnlockTimeTextBox.Text;
                Properties.Settings.Default.Save();

                controlDeviceClient.Close();
                localUDPClient.Close();

                System.Windows.Forms.Application.Exit();
                System.Environment.Exit(1);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        //***********************************************************************
        //  Name: loginButtonClicked
        //  Description: Called when a user attempts to login                    
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void loginButtonClicked(object sender, EventArgs e)
        {
            string user = userNameTextBox.Text;
            string pass = passwordTextBox.Text;
            if (!SendMessage(CONFIG + ":" + LOGIN + "," + user + "," + pass + ";"))
            {
                MessageBox.Show("Login message failed");
            }
        }

        //***********************************************************************
        //  Name: timerEnable set
        //  Description: Used to enable the alert timer                   
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        public bool TimerEnable
        {
            set
            {
                this.Invoke((MethodInvoker)delegate
                {
                    this.alertTimer.Enabled = value;
                });
            }
        }

        //***********************************************************************
        //  Name: timerEnable
        //  Description: Used to enable the timer                    
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        public static void timerEnable()
        {
            var form = Form.ActiveForm as Form1;
            if (form != null)
                form.TimerEnable = true;
        }

        public bool ImageTimerEnable
        {
            set
            {
                this.Invoke((MethodInvoker)delegate
                {
                    this.imageCaptureTimer.Enabled = value;
                });
            }
        }

        //***********************************************************************
        //  Name: imageCaptureEnable
        //  Description: Used to enable the image capture timer                    
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        public static void imageCaptureEnable()
        {
            var form = Form.ActiveForm as Form1;
            if (form != null)
                form.ImageTimerEnable = true;
        }

        //***********************************************************************
        //  Name: formMinimized
        //  Description: Called when the main form is minimized. The window is then
        //              minimized to the system tray and a notification bubble
        //              appears.    
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void formMinimized(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
            {
                Hide();
                secsIcon.BalloonTipTitle = "APP Hidden";
                secsIcon.BalloonTipText = "SECS has beeen minimized to the taskbar.";
                secsIcon.ShowBalloonTip(3000);
            }
        }

        //***********************************************************************
        //  Name: iconDoubleCLick
        //  Description: Used to return the program window to its original state                   
        //  Parameters: none
        //  Author: Scott Duntley (sdd2857@rit.edu)
        //***********************************************************************
        private void iconDoubleClick(object sender, EventArgs e)
        {
            Show();
            WindowState = FormWindowState.Normal;
        }

        private void imageCaptureTimer_Tick(object sender, EventArgs e)
        {
            captureImage();
        }

        private void captureImage()
        {
            try
            {
                // Get the current date and time
                char[] delims = { ':', ' ' };
                String[] tmp = (DateTime.Now.ToLongTimeString()).Split(delims);
                String curr_time = tmp[0] + tmp[1] + "_" + tmp[2] + tmp[3];
                tmp = (DateTime.Now.ToShortDateString()).Split('/');
                String curr_date = tmp[0] + tmp[1] + tmp[2];

                // Creates a web request for the current image displayed on the webcam
                HttpWebRequest requestPic = (HttpWebRequest)WebRequest.Create("http://129.21.27.69:81/image/jpeg.cgi");
                requestPic.Credentials = new NetworkCredential("admin", "secs");
                HttpWebResponse responsePic = (HttpWebResponse)requestPic.GetResponse();

                // Create the image
                Image captureImage = Image.FromStream(responsePic.GetResponseStream());

                String fileSaveLocation = optionsForm.imageCaptureLocationTextBox.Text;

                // Create the directory to save pictures to if it doesn't exist
                if (!Directory.Exists(fileSaveLocation))
                {
                    DirectoryInfo tmp_info = Directory.CreateDirectory(fileSaveLocation);
                }

                // Fixes the problem of top level drive directories already having a \
                if (fileSaveLocation[fileSaveLocation.Length - 1] == '\\')
                {
                    fileSaveLocation = fileSaveLocation.Substring(0, fileSaveLocation.Length - 1);
                }

                // Replace \'s with \\'s for save function, then formats the image name as
                // capture_time_date.bmp
                fileSaveLocation = fileSaveLocation.Replace("\\", "\\") + "\\capture" + "_" + curr_time + "_" + curr_date + ".jpg";
                captureImage.Save(fileSaveLocation);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                MessageBox.Show("Image Capture Location has not been set");
            }
        }
    }
}
